﻿// Deep Earth is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warranty – Use at your own risk
// View the project and the latest code at http://DeepEarth.codeplex.com/

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using DeepEarth.Client.MapControl;
using DeepEarth.Client.MapControl.Geometries;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;

namespace DeepEarth.Client.Controls.DEMap
{
    [TemplatePart(Name = PART_xCoordTextBlock, Type = typeof(TextBlock))]
    [TemplatePart(Name = PART_yCoordTextBlock, Type = typeof(TextBlock))]
    [TemplatePart(Name = PART_zoomTextBlock, Type = typeof(TextBlock))]
    [TemplatePart(Name = PART_gotoLatitudeText, Type = typeof(TextBox))]
    [TemplatePart(Name = PART_gotoLongitudeText, Type = typeof(TextBox))]
    [TemplatePart(Name = PART_gotoButton, Type = typeof(Button))]
    [TemplatePart(Name = PART_toolPanel, Type = typeof(ToolPanel))]
    [TemplatePart(Name = PART_displayModeDecimal, Type = typeof(RadioButton))]
    [TemplatePart(Name = PART_displayModeDDMMSS, Type = typeof(RadioButton))]
    [TemplatePart(Name = PART_displayModeUTM, Type = typeof(RadioButton))]
    public class CoordinatePanel : Control, IMapControl<Map>
    {
        private const string PART_displayModeDDMMSS = "PART_displayModeDDMMSS";
        private const string PART_displayModeDecimal = "PART_displayModeDecimal";
        private const string PART_displayModeUTM = "PART_displayModeUTM";
        private const string PART_gotoButton = "PART_gotoButton";
        private const string PART_gotoLatitudeText = "PART_gotoLatitudeText";
        private const string PART_gotoLongitudeText = "PART_gotoLongitudeText";
        private const string PART_toolPanel = "PART_toolPanel";
        private const string PART_xCoordTextBlock = "PART_xCoordTextBlock";
        private const string PART_yCoordTextBlock = "PART_yCoordTextBlock";
        private const string PART_zoomTextBlock = "PART_zoomTextBlock";

        private static Point _lastMouseLocation;
        private RadioButton _displayModeDdmmss;
        private RadioButton _displayModeDecimal;
        private RadioButton _displayModeUtm;
        private Button _gotoButton;
        private TextBox _gotoLatitudeText;
        private TextBox _gotoLongitudeText;
        private Map _mapInstance;

        private string _mapName;
        private ToolPanel _toolPanel;
        private TextBlock _xCoordTextBlock;
        private TextBlock _yCoordTextBlock;
        private TextBlock _zoomTextBlock;

        public CoordinatePanel()
        {
            DefaultStyleKey = typeof(CoordinatePanel);  
            
            if (!DesignerProperties.IsInDesignTool)
            {
                _lastMouseLocation = new Point();    
                IsEnabled = false;
                Loaded += CoordinatePanel_Loaded;
            }
        }

        #region IMapControl<Map> Members

        public string MapName
        {
            get { return _mapName; }
            set
            {
                _mapName = value;
                SetMapInstance(MapName);
            }
        }

        public Map MapInstance
        {
            get { return _mapInstance; }
            set
            {
                if (_mapInstance != null)
                {
                    _mapInstance.Events.MapMouseMove -= MapMouseMove;
                    _mapInstance.Events.MapViewChanged -= MapViewChangeOnFrame;
                }
                _mapInstance = value;
                if (_mapInstance != null)
                {
                    _mapInstance.Events.MapMouseMove += MapMouseMove;
                    _mapInstance.Events.MapViewChanged += MapViewChangeOnFrame;
                }
            }
        }

        public void Dispose()
        {
            MapInstance = null;

            if (_gotoButton != null)
            {
                _gotoButton.Click -= GotoButtonClick;
            }
        }

        #endregion

        private void CoordinatePanel_Loaded(object sender, RoutedEventArgs e)
        {
            SetMapInstance(MapName);
        }

        private void SetMapInstance(string mapname)
        {
            MapInstance = Utilities.FindVisualChildByName<Map>(Application.Current.RootVisual, mapname);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            if (!DesignerProperties.IsInDesignTool)
            {
                _xCoordTextBlock = (TextBlock)GetTemplateChild(PART_xCoordTextBlock);
                _yCoordTextBlock = (TextBlock)GetTemplateChild(PART_yCoordTextBlock);
                _zoomTextBlock = (TextBlock)GetTemplateChild(PART_zoomTextBlock);
                _gotoButton = (Button)GetTemplateChild(PART_gotoButton);
                _gotoLatitudeText = (TextBox)GetTemplateChild(PART_gotoLatitudeText);
                _gotoLongitudeText = (TextBox)GetTemplateChild(PART_gotoLongitudeText);
                _displayModeDecimal = (RadioButton)GetTemplateChild(PART_displayModeDecimal);
                _displayModeDdmmss = (RadioButton)GetTemplateChild(PART_displayModeDDMMSS);
                _displayModeUtm = (RadioButton)GetTemplateChild(PART_displayModeUTM);
                _toolPanel = (ToolPanel)GetTemplateChild(PART_toolPanel);
                IsEnabled = true;

                if (_gotoButton != null)
                {
                    _gotoButton.Click += GotoButtonClick;
                }

                if (_displayModeDecimal != null)
                {
                    _displayModeDecimal.Click += DisplayModeClick;
                    _displayModeDecimal.IsChecked = true;
                }

                if (_displayModeDdmmss != null)
                {
                    _displayModeDdmmss.Click += DisplayModeClick;
                }

                if (_displayModeUtm != null)
                {
                    _displayModeUtm.Click += DisplayModeClick;
                }

                if (_gotoLatitudeText != null)
                {
                    _gotoLatitudeText.GotFocus += (o, e) => _gotoLatitudeText.SelectAll();
                }

                if (_gotoLongitudeText != null)
                {
                    _gotoLongitudeText.GotFocus += (o, e) => _gotoLongitudeText.SelectAll();
                }

                //set inital values
                SetXYCoordinates(_lastMouseLocation);
                SetZoomLevel();
            }
        }

        private void DisplayModeClick(object sender, RoutedEventArgs e)
        {
            SetXYCoordinates(_lastMouseLocation);
        }

        private void GotoButtonClick(object sender, RoutedEventArgs e)
        {
            //validate input, set map centre.
            double lat;
            double lon;
            if (double.TryParse(_gotoLatitudeText.Text, out lat) && double.TryParse(_gotoLongitudeText.Text, out lon))
            {
                if (lat <= 90 && lat >= -90 && lon <= 180 && lon >= -180)
                {
                    MapInstance.GeoCenter = new Location { Longitude = lon, Latitude = lat };
                }
            }
            if (_toolPanel != null)
            {
                _toolPanel.CloseOptions();
            }
        }

        private void MapViewChangeOnFrame(object sender, EventArgs e)
        {
            if (IsEnabled)
            {
                SetZoomLevel();
            }
        }

        private void MapMouseMove(object sender, MouseEventArgs e)
        {
            if (IsEnabled)
            {
                _lastMouseLocation = e.GetPosition(MapInstance);
                SetXYCoordinates(_lastMouseLocation);
            }
        }

        private void SetZoomLevel()
        {
            _zoomTextBlock.Text = string.Format("{0:N2}", MapInstance.ZoomLevel);
        }

        private void SetXYCoordinates(Point point)
        {
            Location mapCoordinate = MapInstance.CoordHelper.PixelToGeo(point);
            if (_displayModeDecimal.IsChecked.GetValueOrDefault())
            {
                _xCoordTextBlock.Text = string.Format("{0:N4}", mapCoordinate.Longitude);
                _yCoordTextBlock.Text = string.Format("{0:N4},", mapCoordinate.Latitude);
                return;
            }
            if (_displayModeDdmmss.IsChecked.GetValueOrDefault())
            {
                _xCoordTextBlock.Text = Utilities.DDMMSSFromDecimal(mapCoordinate.Longitude)
                    + ((mapCoordinate.Longitude > 0) ? "E " : "W ");
                _yCoordTextBlock.Text = Utilities.DDMMSSFromDecimal(mapCoordinate.Latitude)
                    + ((mapCoordinate.Latitude > 0) ? "N " : "S ");
                return;
            }
            if (_displayModeUtm.IsChecked.GetValueOrDefault())
            {
                //Transform to UTM
                var ctfac = new CoordinateTransformationFactory();
                GeographicCoordinateSystem wgs84Geo = GeographicCoordinateSystem.WGS84;
                var zone = (int)Math.Ceiling((mapCoordinate.Longitude + 180) / 6);
                ProjectedCoordinateSystem utm = ProjectedCoordinateSystem.WGS84_UTM(zone, mapCoordinate.Latitude > 0);
                ICoordinateTransformation trans = ctfac.CreateFromCoordinateSystems(wgs84Geo, utm);
                Point pUtm = trans.MathTransform.Transform(new Point(mapCoordinate.Longitude, mapCoordinate.Latitude));
                _xCoordTextBlock.Text = string.Format("N{0}", Math.Round(pUtm.Y, 0)) +
                                       string.Format(" ({0}{1})", zone, mapCoordinate.Latitude > 0 ? 'N' : 'S');
                _yCoordTextBlock.Text = string.Format("E{0} ", Math.Round(pUtm.X, 0));
                return;
            }
        }
    }
}